﻿namespace PugLib.UI
{
	using System;
	using System.ComponentModel;
	using System.Diagnostics;
	using System.Globalization;
	using System.Linq.Expressions;
	using System.Reflection;

	using PugLib.Extensions;

	/// <summary>
	/// Read more about this class at http://clariusconsulting.net/kzu
	/// </summary>
	[DebuggerStepThrough]
	public abstract class ViewModel : INotifyPropertyChanged
	{
		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion

		protected void RaiseChanged<TProperty>(Expression<Func<TProperty>> propertyExpression)
		{
			MemberExpression property = propertyExpression.Body as MemberExpression;
			if (property == null || !(property.Member is PropertyInfo) || !IsPropertyOfThis(property))
			{
				throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
				                                          "Expression must be of the form 'this.PropertyName'. Invalid expression '{0}'.",
				                                          propertyExpression), "propertyExpression");
			}

			this.OnPropertyChanged(property.Member.Name);
		}

		private bool IsPropertyOfThis(MemberExpression property)
		{
			ConstantExpression constant = RemoveCast(property.Expression) as ConstantExpression;
			return constant != null && constant.Value == this;
		}

		private static Expression RemoveCast(Expression expression)
		{
			if (expression.NodeType == ExpressionType.Convert ||
			    expression.NodeType == ExpressionType.ConvertChecked)
			{
				return ((UnaryExpression)expression).Operand;
			}

			return expression;
		}

		protected virtual void OnPropertyChanged(String propertyName)
		{
			PropertyChanged.Raise(this, new PropertyChangedEventArgs(propertyName));
		}
	}
}